﻿/*****************************************介绍*****************************************
 * 作者：User
 * 创建时间：2025-06-03 18:55:25
 * 功能：常见加解密工具集合：
 *      1、提供AES加解密需求
 *      2、提供DES加解密需求
 *      3、提供TripleDES加解密需求
 *      4、提供RSA加密需求
 *      5、提供SHA256加密需求
 *      6、提供MD5加密需求
**************************************************************************************/

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Game.Framework.Utility
{
    public class CryptoUtils
    {
        #region AES-256-CBC 加密解密

        private const string AES_KEY = "c45022169f4a4064a464763aec7a01d9";

        /// <summary>
        /// AES-256-CBC 加密（PKCS5Padding，IV=Key前16字节，输出Base64）
        /// </summary>
        public static string AesEncrypt(string plaintext)
        {
            if (string.IsNullOrEmpty(AES_KEY))
                throw new Exception(message: "Please first call EncryptionUtil.Init !!");

            byte[] keyBytes = Encoding.UTF8.GetBytes(AES_KEY);
            if (keyBytes.Length != 32)
                throw new ArgumentException("Key length must be exactly 32 bytes (256 bits).");

            byte[] ivBytes = new byte[16];
            Array.Copy(keyBytes, 0, ivBytes, 0, 16);

            using (Aes aes = Aes.Create())
            {
                aes.Key = keyBytes;
                aes.IV = ivBytes;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;

                using (var ms = new MemoryStream())
                using (var cryptoStream = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    byte[] bytes = Encoding.UTF8.GetBytes(plaintext);
                    cryptoStream.Write(bytes, 0, bytes.Length);
                    cryptoStream.FlushFinalBlock();
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        /// <summary>
        /// AES-256-CBC 解密（PKCS5Padding，IV=Key前16字节，输入Base64）
        /// </summary>
        public static string AesDecrypt(string ciphertext)
        {
            if (string.IsNullOrEmpty(AES_KEY))
                throw new Exception(message: "Please first call EncryptionUtil.Init !!");

            byte[] keyBytes = Encoding.UTF8.GetBytes(AES_KEY);
            if (keyBytes.Length != 32)
                throw new ArgumentException("Key length must be exactly 32 bytes (256 bits).");

            byte[] ivBytes = new byte[16];
            Array.Copy(keyBytes, 0, ivBytes, 0, 16);
            byte[] cipherBytes = Convert.FromBase64String(ciphertext);

            using (Aes aes = Aes.Create())
            {
                aes.Key = keyBytes;
                aes.IV = ivBytes;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;

                using (var ms = new MemoryStream(cipherBytes))
                using (var cryptoStream = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cryptoStream, Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        #endregion

        #region DES加密解密

        /// <summary>
        /// DES 加密（AES 出现前常用）。    
        /// 使用场景：
        ///  - 仅用于与遗留系统兼容的场景（例如必须对接只支持DES的第三方）。  
        ///  - 不推荐用于传输敏感数据或新项目，安全性不足。  
        /// 注意：
        ///  - key 必须为 8 字节（64 位，实际有效 56 位）；iv 必须为 8 字节。
        ///  - 如果需要强安全，请使用 AES-256。
        /// </summary>
        /// <param name="plainText">要加密的明文</param>
        /// <param name="key">密钥（必须正好 8 字节，UTF8 编码后长度为 8）</param>
        /// <param name="iv">偏移量（必须正好 8 字节）</param>
        /// <returns>Base64 编码的密文</returns>
        public static string DesEncrypt(string plainText, string key, string iv)
        {
            using (var des = DES.Create())
            {
                des.Key = Encoding.UTF8.GetBytes(key.Substring(0, 8));
                des.IV = Encoding.UTF8.GetBytes(iv.Substring(0, 8));
                des.Mode = CipherMode.CBC;
                des.Padding = PaddingMode.PKCS7;

                using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    byte[] data = Encoding.UTF8.GetBytes(plainText);
                    cs.Write(data, 0, data.Length);
                    cs.FlushFinalBlock();
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        /// <summary>
        /// DES 解密
        /// </summary>
        public static string DesDecrypt(string cipherText, string key, string iv)
        {
            using (var des = DES.Create())
            {
                des.Key = Encoding.UTF8.GetBytes(key.Substring(0, 8));
                des.IV = Encoding.UTF8.GetBytes(iv.Substring(0, 8));
                des.Mode = CipherMode.CBC;
                des.Padding = PaddingMode.PKCS7;

                byte[] data = Convert.FromBase64String(cipherText);

                using (var ms = new MemoryStream(data))
                using (var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cs, Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        #endregion

        #region TripleDES (3DES)加密解密

        /// <summary>
        /// TripleDES (3DES) 加密。
        /// 使用场景：
        ///  - 与只支持 3DES 的遗留系统互通时可用。
        ///  - 3DES 安全性也不如现代对称算法（AES），新系统优先使用 AES。
        /// 注意：
        ///  - key 必须为 16 字节（两钥 112-bit）或 24 字节（三钥 168-bit）；iv 必须为 8 字节。
        /// </summary>
        public static string TripleDesSEncrypt(string plaintext, string key, string iv)
        {
            using (var tdes = TripleDES.Create())
            {
                tdes.Key = Encoding.UTF8.GetBytes(key.Substring(0, 24));
                tdes.IV = Encoding.UTF8.GetBytes(iv.Substring(0, 8));
                tdes.Mode = CipherMode.CBC;
                tdes.Padding = PaddingMode.PKCS7;

                using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(ms, tdes.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    byte[] data = Encoding.UTF8.GetBytes(plaintext);
                    cs.Write(data, 0, data.Length);
                    cs.FlushFinalBlock();
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        /// <summary>
        /// TripleDES 解密
        /// </summary>
        public static string TripleDesDecrypt(string cipherText, string key, string iv)
        {
            using (var tdes = TripleDES.Create())
            {
                tdes.Key = Encoding.UTF8.GetBytes(key.Substring(0, 24));
                tdes.IV = Encoding.UTF8.GetBytes(iv.Substring(0, 8));
                tdes.Mode = CipherMode.CBC;
                tdes.Padding = PaddingMode.PKCS7;

                byte[] data = Convert.FromBase64String(cipherText);

                using (var ms = new MemoryStream(data))
                using (var cs = new CryptoStream(ms, tdes.CreateDecryptor(), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cs, Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        #endregion

        #region RSA加密解密

        /// <summary>
        /// RSA 加密（公钥加密）。
        /// 使用场景：
        ///  - 适合加密短消息（例如 AES 的对称密钥、会话密钥、少量敏感字段等）。  
        ///  - 若需加密大数据，采用混合加密：生成随机对称密钥（例如 AES），用对称密钥加密数据，然后用 RSA 公钥加密该对称密钥（并一并发送）。  
        /// 注意：
        ///  - RSA 可以使用 PKCS#1 v1.5（legacy）或 OAEP（推荐）。OAEP 更安全（推荐 true），但与某些旧平台的兼容性需确认。  
        ///  - 加密长度受限：例如 2048-bit 密钥，PKCS#1 v1.5 最大约 245 字节，OAEP（SHA-1）最大约 214 字节。  
        /// </summary>
        /// <param name="plaintext">要加密的明文（长度必须小于可加密最大字节数）</param>
        /// <param name="publicKeyXml">公钥的 XML 格式（来自 RSACryptoServiceProvider.ToXmlString(false)）</param>
        /// <param name="useOaep">是否使用 OAEP 填充（true 推荐，但与旧平台兼容性需确认）</param>
        /// <returns>Base64 编码的密文</returns>
        public static string RsaEncrypt(string plaintext, string publicKeyXml, bool useOaep = false)
        {
            if (plaintext == null) throw new ArgumentNullException(nameof(plaintext));
            if (string.IsNullOrEmpty(publicKeyXml)) throw new ArgumentException("Public key cannot be null/empty", nameof(publicKeyXml));

            byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext);

            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                // 注意：在 .NET Core/Standard 中，FromXmlString/ToXmlString 可能需要额外扩展方法
                rsa.FromXmlString(publicKeyXml);
                byte[] cipher = rsa.Encrypt(plainBytes, useOaep);
                return Convert.ToBase64String(cipher);
            }
        }

        /// <summary>
        /// RSA 解密（私钥解密）。
        /// </summary>
        public static string RsaDecrypt(string ciphertext, string privateKeyXml, bool useOaep = false)
        {
            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(privateKeyXml);
                byte[] data = Convert.FromBase64String(ciphertext);
                byte[] plain = rsa.Decrypt(data, useOaep);
                return Encoding.UTF8.GetString(plain);
            }
        }

        #endregion

        #region SHA256 哈希（不可逆）

        /// <summary>
        /// 计算字符串的 SHA256 摘要（十六进制小写）。
        /// 使用场景：
        ///  - 校验数据完整性、生成签名前的摘要、对非密钥数据做不可逆摘要等。  
        /// 注意：
        ///  - **不要**直接把 SHA256 用作用户密码的存储哈希（应使用带盐的慢哈希算法，如 bcrypt/Argon2/PBKDF2）。
        /// </summary>
        public static string ToSHA256(string plainText)
        {
            using (var sha256 = SHA256.Create())
            {
                byte[] data = Encoding.UTF8.GetBytes(plainText);
                byte[] hash = sha256.ComputeHash(data);
                StringBuilder sb = new StringBuilder();
                foreach (byte b in hash)
                {
                    sb.Append(b.ToString("x2"));
                }
                return sb.ToString();
            }
        }


        #endregion

        #region MD5相关

        public static string StringToMD5(string str)
        {
            using (MD5 md5 = MD5.Create())
            {
                byte[] bytes = Encoding.UTF8.GetBytes(str);
                byte[] hashBytes = md5.ComputeHash(bytes);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    sb.Append(hashBytes[i].ToString("x2"));
                }
                return sb.ToString();
            }
        }

        public static bool VerifyStringWithMD5(string input, string md5)
        {
            using (var md5Algo = MD5.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5Algo.ComputeHash(inputBytes);
                var sb = new StringBuilder();
                foreach (var b in hashBytes) sb.Append(b.ToString("x2"));
                return string.Equals(sb.ToString(), md5, StringComparison.OrdinalIgnoreCase);
            }
        }

        #endregion
    }
}